home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 …ember: Reference Library / Dev.CD Dec 94.toast / Technical Documentation / Mac Tech Notes (DocViewer) / PT • Platforms & Tools / PT28 Multiple Inheritance / PT28 Multiple Inheritance
Encoding:
Text File  |  1994-10-19  |  33.5 KB  |  94 lines  |  [ONLN/HLX2]

  1. PT 28 - Multiple Inheritance and HandleObjects
  2. Platforms & Tools    
  3. Written by:    Larry Rosenstein    August 1990
  4. This Technical Note answers a common question about MPW C++:  “Why doesn’t HandleObject support multiple inheritance?”  It does this by giving a brief overview of how multiple inheritance is implemented in MPW C++.
  5. What Are HandleObjects Anyway?
  6. MPW C++ contains several extensions to “standard C++” for supporting Macintosh programming.  One such extension is the built-in class HandleObject.  An instance of any class descended from HandleObject is allocated as a handle in the heap.  You refer to one of these instances as if it were a simple pointer; the compiler takes care of the extra dereference required because the object is really a handle.
  7. A HandleObject is useful in Macintosh programming for the same reason a handle is useful.  The use of handles helps prevent heap fragmentation.  The nature of HandleObject imposes some restrictions on how you can use it in a program, however.
  8. First, since each instance is allocated as a handle, it follows that all instances must be allocated on the heap.  (“Native” C++ objects can be allocated on the stack or in the global space as well.)  Consequently, you always declare variables, parameters, etc. to be pointers to the class.  For example:
  9.     class TSample: public HandleObject {
  10.     public:
  11.         …
  12.         long    fData;
  13.     };
  14.     TSample  *aSampleInstance;        // Legal
  15.     TSample  anotherSample;           // Results in a compile-time error
  16. The error message the compiler generates in this case is “Can’t declare a handle/pascal object: anotherSample.”  At first this message might seem strange, because the last two lines in this code seem to both declare objects.  Actually, the first declaration is of a pointer to an object, not of the object itself.
  17. The second restriction is that you must follow the usual rules for manipulating handles.  In particular, you have to be careful about creating pointers to a HandleObject instance variable, since the object might move if the heap is compacted.  If you write
  18.     long *x = & (aSampleInstance -> fData);
  19. then x becomes invalid if the object moves.  The solution in this case is to lock the object if there’s a possibility of the heap being compacted.  Instances of HandleObject are allocated with a call to _NewHandle, so you can use _HLock and _HUnlock to lock and unlock the object.
  20. The third restriction is that you cannot use multiple inheritance with a HandleObject.  The reason behind this restriction is not evident, however.  To understand the reason, you must look at the implementation of multiple inheritance.
  21. Implementing Multiple Inheritance
  22. To understand how multiple inheritance is implemented, one needs a simple example. Suppose you define two classes as follows:
  23.     class TBaseA {
  24.     public:
  25.         virtual void  SetVarA(long newValue);
  26.                 long  fVarA;
  27.         …
  28.     };
  29.     class TBaseB {
  30.     public:
  31.         virtual void  SetVarB(long newValue);
  32.                 long  fVarB;
  33.         …
  34.     };
  35. If you were to look at instances of these classes (see Figure 1), you would find that in each case the instance storage would contain four bytes for the C++ virtual table (vtable) and four bytes for the instance variable.  Any code that accesses the instance variable (for example TBaseB::SetVarB) would do so using a fixed offset from the start of the object.  (In this particular version of C++, this offset was 0; your offset may vary.)
  36. Figure 1–Layout of TBaseA and TBaseB Instances
  37. Now suppose you define another class:
  38.     class TDerived: public TBaseA, public TBaseB {
  39.     public:
  40.         virtual void  SetDerivedVar(long newValue);
  41.                 long  fDerivedVar;
  42.         …
  43.     };
  44. In this case, an instance of TDerived has the following layout:
  45. Figure 2–Layout of TDerived Instance
  46. This is what you would expect.  TDerived inherits from both TBaseA and TBaseB, and therefore instances of TDerived contain a part that is a TBaseA and a part that is a TBaseB.  In addition, the virtual table vtableDerived includes the tables for both TBaseA and TDerived.
  47. TDerived also inherits the methods defined in TBaseA and TBaseB.  Suppose you wanted to call the method SetVarB, using a TDerived object.  The code for SetVarB is expecting to be passed a pointer to a TBaseB object (all methods are passed a pointer to an appropriate object as an implicit parameter), and refers to fVarB by a fixed offset from that pointer.  Therefore, to call SetVarB using a TDerived object, C++ passes a pointer to the middle of the object; specifically it passes a pointer to the part of the object that represents a TBaseB.
  48. This gives you a very basic idea of how C++ implements multiple inheritance.  For more details, read “Multiple Inheritance for C++” by Bjarne Stroustrup in Proceedings EUUG Spring 1987 Conference, Helsinki.
  49. So What About HandleObjects?
  50. The next question is how this implementation imposes a restriction on a HandleObject.  The answer is simple.  Each method of a HandleObject class expects to be passed a handle to the object, instead of a pointer.  But when multiple inheritance is used, the compiler sometimes has to pass a pointer to the middle of the object.  It is not possible to create a valid handle that refers to the middle of another handle.  (Creating a fake handle is a compatibility risk; besides, the pointer into the middle of the handle would be invalid if the handle is moved.)
  51. Designing a new implementation of multiple inheritance that is compatible with a HandleObject, as well as the rest of C++, is a big undertaking.  For that reason, it is unlikely that this restriction will disappear in the future.  There are, however, two alternatives to consider:
  52. Damn the Fragmentation, Full Speed Ahead
  53. The main reason to use a HandleObject is to reduce the chance of fragmentation that would result from using a non-relocatable block.  In a few applications, however, the memory allocation patterns are very predictable, and fragmentation might not be an issue.  In those cases, you can use “native” C++ classes.  (Don’t use the argument that 8 Mb machines are common, and virtual memory is here to stay so fragmentation isn’t an issue at all.  Data always expands to fill the available memory space, real or virtual.)
  54. If you adopt this approach, you should read the article “Using C++ Objects in a Handle-Based World” by Andrew Shebanow in Issue 2 of d e v e l o p, April 1990.  This article describes how you can use native C++ objects and minimize heap fragmentation, by overriding the way C++ normally allocates objects.  The same techniques can be used to customize the way your program allocates certain objects.
  55. “Doctor, It Hurts When I Do That…”
  56. The other alternative is to give up multiple inheritance.  In most cases, this isn’t as difficult as it sounds.  The typical way you would do this is with a form of delegation.  For example, you could rewrite the class TDerived as:
  57.     class TSingleDerived: public TBaseA {
  58.     public:
  59.         virtual void   SetDerivedVar(long newValue);
  60.                 void   SetBaseB(long newValue);
  61.                 long   fDerivedVar;
  62.                 TBaseB fBaseBPart;
  63.         …
  64.     };
  65. In this case TSingleDerived inherits only from TBaseA, but includes an instance of TBaseB as an instance variable.  It also implements the method SetBaseB to call the method by the same name in the TBaseB class.  (In effect, TSingleDerived delegates part of its implementation to TBaseB.)  The advantage of this approach is that it requires only single inheritance, yet you can still reuse the implementation of TBaseB.
  66. The disadvantages are that TSingleDerived is not a subtype of TBaseB, which means that an instance of TSingleDerived cannot be used in a situation that requires a TBaseB.  Also, TSingleDerived has to define a method that corresponds to each method in TBaseB.  (You can, however, define these functions as inline and non-virtual, which eliminates any run-time overhead.)
  67. By The Way…
  68. You should realize that the multiple inheritance implementation previously described costs some extra space, compared to a simpler implementation that does not support multiple inheritance (e.g., the implementation used for a HandleObject).  Each vtable is twice as large, and each method call takes about 24 bytes, compared to 14.  This is true even if you do not take advantage of multiple inheritance.  For this reason, MPW C++ also contains a built in class called SingleObject, whose instances are allocated in the same way as normal C++ instance, but which only supports single inheritance.  (By the way, the third class built into MPW C++, PascalObject, uses Object Pascal’s run-time implementation, which takes the least amount of space, but the most execution time.)
  69. Conclusion
  70. You cannot use a HandleObject with multiple inheritance, because of the way multiple inheritance is implemented in MPW C++.  Your alternatives are to give up one or the other.  You can either use native C++ objects and let the objects fall where they may, or give up multiple inheritance and use a form of delegation.
  71. Further Reference:
  72. •    MPW C++ Reference Manual
  73. •    “Using C++ Objects in a Handle-Based World,” Andrew Shebanow, d e v e l o p, Issue 2, April 1990.
  74. •    “Multiple Inheritance for C++,” Bjarne Stroustrup, Proceedings EUUG Spring 1987 Conference, Helsinki.
  75. ΔHRˇ ˇˇˇˇRH°d WORDS †å°d WORDR…†Ç
  76. /ZÅ#
  77.     0Ià:µú9"{    ˇˇˇˇˇˇˇˇ#†ƒ°d
  78. ONLNf˛†å°d1drw2…-·_ġˇˇˇˇˇè°ñ x°ddrw2:°ddrw2:$°d4drw2:°öˇ˙ó@†ò,Times
  79. .WIQkWIQk+]BNew Technical Notes†ô°ddrw2:°„†ó°d1drw2eÙġˇˇˇˇˇP°ñ x°ddrw2:°ddrw2:$°d4drw2:°öˇ˚ÄE¿†ò
  80. Ä({ïDeveloper Support†ô°ddrw2:°„†ó°d`drw2-ÔˇˇˇˇˇˇKÔ- Z  ffZ°d1drw2 ¿˙ÈˇˇˇˇˇˇK°ñ x°ddrw2:°ddrw2:$°d4drw2:0°öˇÙĆò
  81. 0(UÔ†ô°ddrw2:°„†ó°d1drw2ÔÊ˙ˇˇˇˇˇˇ°ñ x°ddrw2:°ddrw2:$°d4drw2:    °öˇ˝Ä†ò
  82.     l+&    ®†ô°ddrw2:°„†ó°d1drw2Â-¯yˇˇˇˇˇˇ°ñ x°ddrw2:°ddrw2:$°d4drw2:°öˇ˚Ä%†ò
  83. BÄ(Z\    Macintosh†ô°ddrw2:°„†ó†ç°ddrw2D†É°dWORD†ç
  84. HR.°dONLNdéZ°Á(úZ.PT 28 - Multiple Inheritance and HandleObjects
  85. °dONLNd/†ZØ÷*Platforms & Tools
  86. °dONLNdBªZ«è* Written by:°dONLNdNª¢«Û)HLarry Rosenstein°dONLNd_ªfl«(ƒfl August 1990°dONLNdk”Zfl™(‹ZThis Technical °dONLNdz”™fl)P;Note answers a common question about MPW C++:  “Why doesn’t,
  87. Courier°dONLNd∂flZÎÆ(ÈZ HandleObject°dONLNd¬‡ÆÏ)T support multiple °dONLNd‘‡Ï)V9inheritance?”  It does this by giving a brief overview of°dONLNdÏZ¯`(ıZ3how multiple inheritance is implemented in MPW C++. X
  88. °dONLNdBZ,>*4What Are HandleObjects Anyway?
  89. °dONLNda8ZDt*1MPW C++ contains several extensions to “standard °dONLNdí8tD(AtC++” for supporting Macintosh°dONLNd∞EZQ(NZ$programming.  One such extension is °dONLNd‘EQa)¥the built-in class °dONLNdÁDaPµ)S HandleObject°dONLNdÛEµQ)T.  An instance of any°dONLNd    RZ^¬([Zclass descended from °dONLNdQ¬])h HandleObject°dONLNd*R^$)T is °dONLNd.R$^)4allocated as a handle in the heap.  You refer to one°dONLNdc^Zjx(gZ@of these instances as if it were a simple pointer; the compiler °dONLNd£^xj(gx#takes care of the extra dereference°dONLNd«jZv/(sZ/required because the object is really a handle.°dONLNd˜ÉZèg*A °dONLNd˘Çgéª)
  90. HandleObject°dONLNdɪè3)T is useful in Macintosh °dONLNdÉ3è)x+programming for the same reason a handle is°dONLNdIêZúÉ(ôZ?useful.  The use of handles helps prevent heap fragmentation.  °dONLNdàêÉú»(ôÉThe nature of °dONLNdñè»õ)E HandleObject°dONLNd£úZ®≤(•ZFimposes some restrictions on how you can use it in a program, however.°dONLNdÍ¥Z¿N*8First, since each instance is allocated as a handle, it °dONLNd"¥N¿)Ù,follows that all instances must be allocated°dONLNdO¿ZÃ"(…Z(on the heap.  (“Native” C++ objects can °dONLNdw¿"Ã)»3be allocated on the stack or in the global space as°dONLNd´ÃZÿÔ(’ZWwell.)  Consequently, you always declare variables, parameters, etc. to be pointers to °dONLNdÃÔÿ(’Ô
  91. the class.°dONLNdÿZ‰ò(·Z For example:
  92.     °dONLNdZ˚"*(    class TSample: public HandleObject {°dONLNdD˙Zë*
  93.     public:°dONLNdPZá*
  94.             …°dONLNdZZ»*
  95.         long    fData;°dONLNdqZ#x*
  96.     };°dONLNdx.Z9@*.    TSample  *aSampleInstance;        // Legal°dONLNdß8ZC¬*
  97. H    TSample  anotherSample;           // Results in a compile-time error
  98. °dONLNdNZZ¥*The error message °dONLNdN¥Z)ZMthe compiler generates in this case is “Can’t declare a handle/pascal object:°dONLNdPZZf‚(cZPanotherSample.”  At first this message might seem strange, because the last two °dONLNd†Z‚f(c‚
  99. lines in this°dONLNdÆfZr¨(oZLcode seem to both declare objects.  Actually, the first declaration is of a °dONLNd˙f¨rÕ(o¨pointer°dONLNdfÕr
  100. )! to an object, °dONLNdf
  101. r)@not°dONLNdrZ~≤({Zof the object itself.°dONLNd*äZñ*(The second restriction is that you must °dONLNdRäñ)¿4follow the usual rules for manipulating handles.  In°dONLNdáóZ£õ(†Z@particular, you have to be careful about creating pointers to a °dONLNd«ñõ¢Ô(†õ HandleObject°dONLNd”óÔ£)T     instance°dONLNd›£ZØø(¨ZMvariable, since the object might move if the heap is compacted.  If you write ΩXΩ
  102. *).PT 28 - Multiple Inheritance and HandleObjects(’1) of 5(ÎZPlatforms & Toolsˇ°¿Ù%%DSIDICT:_cv
  103. currentdict /bu known {bu}if
  104. userdict /_cv known not{userdict /_cv 30 dict put}if
  105. _cv begin
  106. /bdf{bind def}bind def
  107. currentscreen/cs exch def/ca exch def/cf exch def
  108. /setcmykcolor where{/setcmykcolor get /cvcmyk exch def}{/cvcmyk{1 sub 4 1 roll 3{3 index add neg dup 0 lt{pop 0}if 3 1 roll}repeat setrgbcolor pop}bdf }ifelse
  109. /ss{//cf //ca //cs setscreen}bdf
  110. /stg{ss setgray}bdf
  111. /strgb{ss setrgbcolor}bdf
  112. /stcmyk{ss cvcmyk}bdf
  113. /min1{dup 0 eq{pop 1}if}bdf
  114. end
  115. currentdict /bn known {bn}if
  116. †øäHRˇ ˇˇˇˇRH
  117. HR,Times
  118. .+6-Macintosh Technical Notes /4/˘,
  119. Courier
  120.     °dONLNdH6S
  121. *#+    long *x = & (aSampleInstance -> fData);
  122. °dONLNd,_6kN*then °dONLNd1^NjU)x°dONLNd2_UkΩ)L becomes invalid if the object moves.  The solution in this case is to lock °dONLNd~_Ωk¯(hΩ
  123. the object if°dONLNdål6xÈ(u6(there’s a possibility of the heap being °dONLNd¥lÈxe)≥compacted.  Instances of °dONLNdÕkewπ)| HandleObject°dONLNdŸlπx¯)T are allocated°dONLNdËy6Öu(Ç6with a call to °dONLNd˜xuѪ)?
  124. _NewHandle°dONLNdyªÖÂ)F    , so you °dONLNd
  125. yÂÖ
  126. )*can use °dONLNdx
  127. Ñ7)(_HLock°dONLNdy7ÖP)* and °dONLNdxPÑà)_HUnlock°dONLNd%yàÖ¯)8 to lock and unlock the°dONLNd=Ö6ëV(é6object.°dONLNdEû6™°*The third restriction is °dONLNd^û°™Ü)k0that you cannot use multiple inheritance with a °dONLNdéùÜ©⁄) HandleObject°dONLNdöû⁄™¯)T.  The°dONLNd°™6∂?(≥68reason behind this restriction is not evident, however. °dONLNdŸ™?∂¯(≥?# To understand the reason, you must°dONLNd˝∂6¬"(ø63look at the implementation of multiple inheritance.
  128. °dONLNd1⁄6È*'!Implementing Multiple Inheritance
  129. °dONLNdSı6t*ATo understand how multiple inheritance is implemented, one needs °dONLNdîıt¯(˛ta simple example. Suppose°dONLNdÆ6
  130. ÿ(
  131. 6"you define two classes as follows:
  132.     °dONLNd—6$ê*    class TBaseA {°dONLNd‰#6.m*
  133.     public:°dONLNd-68*
  134. -        virtual void  SetVarA(long newValue);°dONLNd76B¬*
  135.                 long  fVarA;°dONLNd;A6Lc*
  136.             …°dONLNdEK6VT*
  137.     };°dONLNdL_6jê*    class TBaseB {°dONLNd_i6tm*
  138.     public:°dONLNdks6~*
  139. -        virtual void  SetVarB(long newValue);°dONLNdô}6à¬*
  140.                 long  fVarB;°dONLNd∂á6íc*
  141.             …°dONLNd¿ë6úT*
  142.     };
  143. °dONLNd«ß6≥ß*LIf you were to look at instances of these classes (see Figure 1), you would °dONLNdßß≥¯(∞ßfind that in each°dONLNd%¥6¿û(Ω6Ncase the instance storage would contain four bytes for the C++ virtual table (°dONLNds≥ûø»(Ωûvtable°dONLNdy¥»¿œ)*) °dONLNd{¥œ¿¯)and four°dONLNdÑ¿6Ãb(…6=bytes for the instance variable.  Any code that accesses the °dONLNd¡¿bï(…binstance variable (for example°dONLNd‡Ã6ÿü(÷6TBaseB::SetVarB°dONLNdÔÕüŸ«)i) would °dONLNd˜Õ«Ÿ¯)(Bdo so using a fixed offset from the start of the object.  (In this°dONLNd:Ÿ6Ât(‚6Dparticular version of C++, this offset was 0; your offset may vary.)†Ç
  144. ÒÖ"©    1Ûá
  145. ¸    ˇˇˇˇˇˇˇˇ0ÚÜ ˝°ñ °öˇ¸
  146. ˜≠÷
  147.     aSD    aSD°dONLNdˇˇ+x fVarA†ó
  148. ÒÖ"©    1
  149. á ¸    ˇˇˇˇˇˇˇˇ0    Ü!˝    1Û2
  150. ß    ˇˇˇˇˇˇˇˇ0Ú1 ®    1
  151. 2 ß    ˇˇˇˇˇˇˇˇ0    1!®°ñ °öˇ¸
  152. 
  153. •fi°dONLNdˇˇ(¶vtableA†ó°dMDPL
  154. ÒÖ"©    1˜XŰñ °öˇ¸
  155. ˜XŰdONLNdˇˇ(YfVarB†ó°dMDPL°dMDPL
  156. ÒÖ"©1
  157. Pâ°ñ °öˇ¸
  158. 
  159. Pâ°dONLNdˇˇ(QvtableB†ó°dMDPL†É
  160. HR
  161. °dONLNd.á:¶(7á.Figure 1–Layout of TBaseA and TBaseB Instances°dONLNdÆF6R(O6%Now suppose you define another class:
  162.     °dONLNd‘^6i0*2    class TDerived: public TBaseA, public TBaseB {°dONLNdh6sm*
  163.     public:°dONLNdr6}5*
  164. 3        virtual void  SetDerivedVar(long newValue);°dONLNdG|6á‡*
  165. "                long  fDerivedVar;°dONLNdjÜ6ëc*
  166.             …°dONLNdtê6õT*
  167.     };    ˇˇˇˇˇˇˇˇ Ω4Ω˘
  168. *=2) of 5)˛.PT 28 - Multiple Inheritance and HandleObjects+sPlatforms & Toolsˇ¬HRˇ ˇˇˇˇRH
  169. HR,Times
  170. .+Z-Developer Support Center(-Ë August 1990 /X/
  171. °dONLNd=ZI⁄(FZIn this case, an instance of ,
  172. Courier°dONLNd<⁄H)ÄTDerived°dONLNd%=Iä)8 has the following layout:†Ç†å
  173. U˝Ãx
  174. 4Xnu0Wˇpv°ñ °öˇ˝†ò
  175. w{w{°dONLNdˇˇ+!fVarA†ô†ó4pÑt0nˇÜv4Üõt0Ñˇùv4ù±t0õˇ≥v4≥»t0±ˇ v°ñ °öˇ˝4†ò°dONLNdˇˇ(}
  176. vtableDerived†ô†ó°ñ °öˇ˝,†ò°dONLNdˇˇ+D fDerivedVar†ô†ó4ä'óN°ñ °öˇ˝†ò°dONLNdˇˇ(î'fVarB†ô†ó4†≠V°ñ °öˇ˝†ò°dONLNdˇˇ(™vtableB†ô†ó†ç†É
  177. HR
  178. °dONLNd@ÿŒ‰ß(·Œ$Figure 2–Layout of TDerived Instance°dONLNdeÒZ˝¸(˙Z This is what you would expect.  °dONLNdÖ¸¸4)¢TDerived°dONLNdçÒ4˝ì)8 inherits from both °dONLNd°ì¸Ω)_TBaseA°dONLNdßÒΩ˝ÿ)* and °dONLNd¨ÿ¸)TBaseB°dONLNd≤Ò˝)*, and°dONLNd∏˛Z
  179. ¬(Ztherefore instances of °dONLNdœ˝¬    ˙)hTDerived°dONLNd◊˛˙
  180. f)8 contain a part that is a °dONLNdÒ˝f    ê)lTBaseA°dONLNd˜˛ê
  181. ƒ)*  and a part °dONLNd˛ƒ
  182. Ó)4
  183. that is a °dONLNd
  184. ˝Ó    )*TBaseB°dONLNd˛
  185. )*.°dONLNd ZÈ(ZIn addition, the virtual table °dONLNd5
  186. ÈD)è
  187. vtableDerived°dONLNdB D€)[ includes the tables for both °dONLNd`
  188. €)óTBaseA°dONLNdf )* and°dONLNdkZ#í(!ZTDerived°dONLNdsí$ñ)8.°dONLNdu0Z<í(:ZTDerived°dONLNd}1í=?)8& also inherits the methods defined in °dONLNd£0?<i)≠TBaseA°dONLNd©1i=l)* °dONLNd™1l=Å)and °dONLNdÆ0Å<´)TBaseB°dONLNd¥1´=)*.  Suppose you wanted°dONLNd >ZJ∞(GZto call the method °dONLNd›=∞I·)VSetVarB°dONLNd‰>·J
  189. )1
  190. , using a °dONLNdÓ=
  191. IE),TDerived°dONLNdˆ>EJú)8 object.  The code °dONLNd    >úJÆ)Wfor °dONLNd
  192. =ÆIfl)SetVarB°dONLNd>flJ)1
  193.  is expecting°dONLNd"KZWü(TZto be passed a °dONLNd1KüW⁄)E
  194. pointer to a °dONLNd>J⁄V);TBaseB°dONLNdDKW)*; object (all methods are passed a pointer to an appropriate°dONLNdÄXZdA(aZ0object as an implicit parameter), and refers to °dONLNd∞WAcd)ÁfVarB°dONLNdµXddÑ)# by a °dONLNdªXÑd) fixed offset from that pointer.°dONLNd‹eZqÆ(nZTherefore, to call °dONLNdÔdÆpfl)TSetVarB°dONLNdˆeflq)1     using a °dONLNdˇdp?)(TDerived°dONLNde?q~)8
  195.  object, C++ °dONLNde~q)?!passes a pointer to the middle of°dONLNd6rZ~⁄({ZYthe object; specifically it passes a pointer to the part of the object that represents a °dONLNdèq⁄}({⁄TBaseB°dONLNdïr~)*.°dONLNdóäZñÌ(ìZNThis gives you a very basic idea of how C++ implements multiple inheritance.  °dONLNdÂäÌñ(ìÌFor more°dONLNdÓñZ¢Å(üZ    details, °dONLNd˜ñÅ¢∫)'<read “Multiple Inheritance for C++” by Bjarne Stroustrup in °dONLNd3ñ∫¢(ü∫Proceedings EUUG°dONLNdD¢ZÆŒ(´ZSpring 1987 Conference°dONLNdZ¢ŒÆ)t , Helsinki.
  196. °dONLNdfΔZ’((“ZSo What About HandleObjects?
  197. °dONLNdÉ‚ZÓà*    The next °dONLNdå‚àÓƒ).?question is how this implementation imposes a restriction on a °dONLNdÀ·ƒÌ(΃ HandleObject°dONLNd◊‚Ó)T.°dONLNd⁄ÔZ˚(¯Z(The answer is simple.  Each method of a °dONLNdÓ˙q)√ HandleObject°dONLNdÔq˚¥)T class expects °dONLNdÔ¥˚)Cto be passed a handle°dONLNd3˚Zh(Zto °dONLNd6˚h)Vthe object, instead of a pointer.  But when multiple inheritance is used, the compiler°dONLNdçZ(Z'sometimes has to pass a pointer to the °dONLNd¥)µ;middle of the object.  It is not possible to create a valid°dONLNdZ»(ZMhandle that refers to the middle of another handle.  (Creating a fake handle °dONLNd=»(»is a compatibility°dONLNdPZ+((Zbrisk; besides, the pointer into the middle of the handle would be invalid if the handle is moved.)°dONLNd≥7ZCQ*+Designing a new implementation of multiple °dONLNdfi7QC)˜%inheritance that is compatible with a°dONLNdCZOÆ(MZ HandleObject°dONLNdDÆP∂)T, °dONLNdD∂P)Ias well as the rest of C++, is a big undertaking.  For that reason, it is°dONLNd\PZ\›(YZunlikely that this restriction °dONLNd{P›\)ÉCwill disappear in the future.  There are, however, two alternatives°dONLNdø\Zhë(eZ to consider:°dONLNdÃtZÄQ*(Damn the Fragmentation, Full Speed Ahead°dONLNdıçZô“*The main reason to use a °dONLNdå“ò&)x HandleObject°dONLNdç&ôñ)T is to reduce the chance °dONLNd3çñô)pof fragmentation that would°dONLNdOôZ•y(¢Zresult °dONLNdVôy•)Ofrom using a non-relocatable block.  In a few applications, however, the memory°dONLNd¶•Z±ã(ÆZ allocation °dONLNd±•ã±)1Qpatterns are very predictable, and fragmentation might not be an issue.  In those ΩXΩ
  198. (’Z.PT 28 - Multiple Inheritance and HandleObjects(’3) of 5(ÎZPlatforms & ToolsˇÙHRˇ ˇˇˇˇRH
  199. HR,Times
  200. .+6-Macintosh Technical Notes /4/˘
  201. °dONLNd<6H**cases, you can use “native” C++ classes.  °dONLNd*<H¯)–.(Don’t use the argument that 8 Mb machines are°dONLNdYH6Tß(Q6Ncommon, and virtual memory is here to stay so fragmentation isn’t an issue at °dONLNdßHßT¯(Qßall.  Data always°dONLNdπT6`K(]6=expands to fill the available memory space, real or virtual.)°dONLNd˜l6x8*8If you adopt this approach, you should read the article °dONLNd/l8x¯(u8$“Using C++ Objects in a Handle-Based°dONLNdTx6Ñ\(Å6World” °dONLNd[x\Ñ)&!by Andrew Shebanow in Issue 2 of °dONLNd|xÑK)≤
  202. d e v e l o p°dONLNdâxKѯ)=%, April 1990.  This article describes°dONLNdØÑ6ê¡(ç6how you can use native C++ °dONLNd Ñ¡ê¯)ã>objects and minimize heap fragmentation, by overriding the way°dONLNd    ê6úœ(ô6 C++ normally allocates objects. °dONLNd)êœú¯)ô: The same techniques can be used to customize the way your°dONLNddú6®”(•6"program allocates certain objects.°dONLNdá¥6¿    *"“Doctor, It Hurts When I Do That…”°dONLNd™Ã6ÿ‰*bThe other alternative is to give up multiple inheritance.  In most cases, this isn’t as difficult °dONLNd Éÿ¯(’‰as it°dONLNdÿ6‰w(·6
  203. sounds.  The °dONLNdÿw‰¯)AMtypical way you would do this is with a form of delegation.  For example, you°dONLNdmÂ6Ò¢(Ó6could rewrite the class ,
  204. Courier°dONLNdÖ‰¢⁄)lTDerived°dONLNdçÂ⁄ÒÍ)8 as:
  205.     °dONLNdí˝6(6)    class TSingleDerived: public TBaseA {°dONLNdº6m*
  206.     public:°dONLNd»6:*
  207. 4        virtual void   SetDerivedVar(long newValue);°dONLNd˝6&!*
  208. /                void   SetBaseB(long newValue);°dONLNd-%60Â*
  209. #                long   fDerivedVar;°dONLNdQ/6:‡*
  210. "                TBaseB fBaseBPart;°dONLNdt96Dc*
  211.             …°dONLNd~C6NT*
  212.     };
  213. °dONLNdÖZ6ft*
  214. In this case °dONLNdíYte÷)>TSingleDerived°dONLNd†Z÷f)b
  215.  inherits °dONLNd™Zf;)-
  216. only from °dONLNd¥Y;ee)8TBaseA°dONLNd∫Zef¯)*, but includes an instance of°dONLNdÿf6r`(p6TBaseB°dONLNdfig`sf)*9 as an instance variable.  It also implements the method °dONLNdffrû(pfSetBaseB°dONLNdgûs¯)8 to call the method°dONLNd3t6ÄE(}6by °dONLNd6tEÄØ)the same name in the °dONLNdKsØŸ)jTBaseB°dONLNdQtŸÄ4)* class.  (In effect, °dONLNdfs4ñ)[TSingleDerived°dONLNdttñį)b delegates part of its°dONLNdãÅ6çè(ä6implementation to °dONLNdùÄèåπ)YTBaseB°dONLNd£Åπç…)*.)  °dONLNdßÅ…ç¯)>The advantage of this approach is that it requires only single°dONLNdÊé6öB(ó6;inheritance, yet you can still reuse the implementation of °dONLNd!çBôl(óBTBaseB°dONLNd'élöp)*.°dONLNd)ß6≥µ(∞6The disadvantages are that °dONLNdD¶µ≤)TSingleDerived°dONLNdRß≥t)b is not a subtype of °dONLNdg¶t≤û)]TBaseB°dONLNdmßû≥≈)*, which °dONLNduß≈≥¯)'
  217. means that°dONLNdÄ¥6¿Ä(Ω6an instance of °dONLNdè≥Äø‚)JTSingleDerived°dONLNdù¥‚¿N)b cannot be used in a °dONLNd≤¥N¿ )lsituation that requires a °dONLNdÃ≥ øÙ)|TBaseB°dONLNd“¥Ù¿¯)*.°dONLNd’¡6ÕV( 6Also, °dONLNd€¿VÃ∏) TSingleDerived°dONLNdÈ¡∏Õ¯)b: has to define a method that corresponds to each method in°dONLNd$Õ6Ÿ`(◊6TBaseB°dONLNd*Œ`⁄®)*
  218. .  (You can, °dONLNd7Œ®⁄¯)H@however, define these functions as inline and non-virtual, which°dONLNdx⁄6Ê⁄(„6"eliminates any run-time overhead.)°dONLNdõÚ6˛â* By The Way…°dONLNdß
  219. 6ó*You should realize °dONLNd∫
  220. ó¯)aGthat the multiple inheritance implementation previously described costs°dONLNd6"
  221. (6(some extra space, compared to a simpler °dONLNd*
  222. "¯)◊-implementation that does not support multiple°dONLNdX#6/(,61inheritance (e.g., the implementation used for a °dONLNdâ".l)‚ HandleObject°dONLNdï#l/ó)T    ).  Each °dONLNdû"ó.¡)+vtable°dONLNd§#¡/¯)*  is twice as°dONLNd±/6;Q(86;large, and each method call takes about 24 bytes, compared °dONLNdÏ/Q;¯(8Q#to 14.  This is true even if you do°dONLNd    ;6GQ(D6>not take advantage of multiple inheritance.  For this reason, °dONLNd    N;QG¯(DQ MPW C++ also contains a built in°dONLNd    oH6TP(Q6class °dONLNd    uHPTo)called °dONLNd    |GoS√) SingleObject°dONLNd    àH√T¯)T=, whose instances are allocated in the same way as normal C++°dONLNd    ΔT6`e(]6?instance, but which only supports single inheritance.  (By the °dONLNd
  223. Te`¯(]eway, the third class built into°dONLNd
  224. %a6mr(j6    MPW C++, °dONLNd
  225. .`rlΔ)< PascalObject°dONLNd
  226. :aΔm)T, uses Object °dONLNd
  227. Ham¯)@1Pascal’s run-time implementation, which takes the°dONLNd
  228. zm6y+(v64least amount of space, but the most execution time.) Ω4Ω˘
  229. *_4) of 5)˛.PT 28 - Multiple Inheritance and HandleObjects+sPlatforms & ToolsˇxHRˇ ˇˇˇˇRH
  230. HR,Times
  231. .+Z-Developer Support Center(-Ë August 1990 /X/
  232. °dONLNdHZW£(TZ
  233. Conclusion
  234. °dONLNd dZp∂*You cannot use a ,
  235. Courier°dONLNdc∂o
  236. )\ HandleObject°dONLNd(d
  237. p‹)T+ with multiple inheritance, because of the °dONLNdSd‹p)“ way multiple°dONLNd`pZ|»(yZIinheritance is implemented in MPW C++.  Your alternatives are to give up °dONLNd©p»|(y»one or the other.°dONLNdº|Zàr(ÖZYou °dONLNd¿|rà)Ucan either use native C++ objects and let the objects fall where they may, or give up°dONLNdàZîC(ëZ2multiple inheritance and use a form of delegation.°dONLNdI∏Zƒƒ*0Further Reference: ƒXƒ°dONLNd\≈l—p+
  238. •°dONLNd^≈~— )MPW C++ Reference Manual°dONLNdw—l›p(⁄l•°dONLNdy—~›˚)“Using C++ Objects in a °dONLNdë—˚›’)}&Handle-Based World,” Andrew Shebanow, °dONLNd∑—’›)⁄
  239. d e v e l o p°dONLNdƒ—›)C,°dONLNdΔ›~Èfi(Ê~Issue 2, April 1990.°dONLNd€Èlıp(Úl•°dONLNd›È~ı>)'“Multiple Inheritance for C++,” Bjarne °dONLNdÈ>ıy)¿ Stroustrup, °dONLNdÈyı);Proceedings EUUG Spring 1987°dONLNd-ı~¥(˛~
  240. Conference°dONLNd7ı¥Á)6 , Helsinki. ΩXΩ
  241. (’Z.PT 28 - Multiple Inheritance and HandleObjects(’5) of 5(ÎZPlatforms & Toolsˇ